/** * Java Diagram Package; An extremely flexible and fast multipurpose diagram component for Swing. Copyright (C) 2001 Eric Crahen <crahen@cse.buffalo.edu> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package diagram; import java.awt.Color; import java.awt.Component; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import javax.swing.BorderFactory; import javax.swing.UIManager; import diagram.figures.PolyLink; import diagram.shape.ArrowHead; /** * @class DefaultLinkRenderer * * @date 08-20-2001 * @author Eric Crahen * @version 1.0 * * Default renderer for links */ public class DefaultLinkRenderer extends DefaultFigureRenderer { protected AffineTransform at = new AffineTransform(); protected GeneralPath path = null; protected Point2D p1; protected Point2D p2; protected FontMetrics font; private Rectangle labelBounds = new Rectangle(); private DefaultLabelRenderer labelRenderer; private Component labelComponent; static { UIManager.put("link.foreground", Color.black); UIManager.put("link.background", Color.white); } /** * Create a new renderer for SimpleLink figures */ public DefaultLinkRenderer() { this(new DefaultLabelRenderer()); } /** * Create a new renderer for SimpleLink figures */ public DefaultLinkRenderer(DefaultLabelRenderer labelRenderer) { setBackground(UIManager.getColor("link.background")); setForeground(UIManager.getColor("link.foreground")); labelRenderer.setBorder(BorderFactory.createEmptyBorder(1,1,1,1)); labelRenderer.setOpaque(false); this.labelRenderer = labelRenderer; } /** * Make sure the decorated bounds are wide enough or tall enough for the label. */ public Rectangle2D getDecoratedBounds(Diagram diagram, Figure figure, Rectangle2D rcBounds) { rcBounds = super.getDecoratedBounds(diagram, figure, rcBounds); // Force the label renderer to update labelComponent = labelRenderer.getRendererComponent(diagram, figure, false); if(labelRenderer.getText() != null) { // Update the label bounds labelBounds = (Rectangle)labelRenderer.getDecoratedBounds(diagram, figure, labelBounds); double x = Math.min(rcBounds.getX(), labelBounds.x); double y = Math.min(rcBounds.getY(), labelBounds.y); double w = Math.max(rcBounds.getWidth(), (labelBounds.x - x) + labelBounds.width); double h = Math.max(rcBounds.getHeight(), (labelBounds.y - y) + labelBounds.height); rcBounds.setFrame(x, y, w, h); } else labelComponent = null; return rcBounds; } public void paintComponent(Graphics g) { PolyLink link = (PolyLink)getFigure(); Graphics2D g2 = (Graphics2D)g; GeneralPath path; double x1, x2, y1, y2; double angle; if(link != null) { // Get the offsets to start drawing this link double dx = getX(); double dy = getY(); g2.setPaint(getForeground()); p2 = link.getPN(1, p2); p1 = link.getSource().getConnection(p2, p1); x1 = p1.getX() - dx; x2 = p2.getX() - dx; y1 = p1.getY() - dy; y2 = p2.getY() - dy; // Draw 1st line segment g2.drawLine((int)x1, (int)y1, (int)x2, (int)y2); // Draw source angle = getSourceAngle(x2, y2, x1, y1); if((path = getSourceEndpoint(x1, y1, this.path)) != null) { at.setToRotation(angle+Math.PI, x1, y1); paintSourceEndpoint(g2, at, path); } p2 = link.getPN(link.getPointCount() - 2, p2); p1 = link.getSink().getConnection(p2 , p1); x1 = p1.getX() - dx; x2 = p2.getX() - dx; y1 = p1.getY() - dy; y2 = p2.getY() - dy; angle = getSinkAngle(x2, y2, x1, y1); // Draw 2nd line segment if(link.getSegmentCount() > 1) g2.drawLine((int)x1, (int)y1, (int)x2, (int)y2); // Draw sink if((path = getSinkEndpoint(x1, y1, this.path)) != null) { at.setToRotation(angle+Math.PI, x1, y1); paintSinkEndpoint(g2, at, path); } // Draw any odd segments left over for(int i = 1; i < link.getSegmentCount() - 1; i++) g2.drawLine((int)(link.getXN(i)-dx), (int)(link.getYN(i)-dy), (int)(link.getXN(i+1)-dx), (int)(link.getYN(i+1)-dy)); // Find the longest link, paint a label there if there is one if(labelComponent != null) { dx = labelBounds.x-getX(); dy = labelBounds.y-getY(); g2.translate(dx, dy); labelComponent.setBounds(labelBounds.x, labelBounds.y, labelBounds.width, labelBounds.height); labelComponent.paint(g); g2.translate(-dx, -dy); } } } /** * Paint the source endpoint. * * @param Graphics2D * @param AffineTransform recommended transformation to rotate the endpoint correctly * @param GeneralPath recommended path to use when rendering the endpoint. */ protected void paintSourceEndpoint(Graphics2D g2, AffineTransform at, GeneralPath path) { path.transform(at); g2.fill(path); } /** * Paint the sink endpoint. * * @param Graphics2D * @param AffineTransform recommended transformation to rotate the endpoint correctly * @param GeneralPath recommended path to use when rendering the endpoint. */ protected void paintSinkEndpoint(Graphics2D g2, AffineTransform at, GeneralPath path) { path.transform(at); g2.fill(path); } /** * x1, y1 will be source connection coords */ protected double getSourceAngle(double x1, double y1, double x2, double y2) { return getAngle(x1, y1, x2, y2); } /** * x1, y1 will be sink connection coords */ protected double getSinkAngle(double x1, double y1, double x2, double y2) { return getAngle(x1, y1, x2, y2); } /** * Calculate the angle between two points. * * @param double * @param double * @param double * @param double * * @return double angle in radians */ final private double getAngle(double x1, double y1, double x2, double y2) { double angle; if((x2 - x1) == 0) { angle = Math.PI/2; return (y2 < y1) ? (angle + Math.PI) : angle; } angle = Math.atan((y1-y2)/(x1-x2)); return (x2 < x1) ? (angle + Math.PI) : angle; } /** * Create the GeneralPath for a figure to be used as the source endpoint. * * @param double x coordinate * @param double y coordinate * @param GeneralPath reuse a path * * @return GeneralPath */ protected GeneralPath getSourceEndpoint(double x, double y, GeneralPath path) { return ArrowHead.createArrowHead(7.0, ArrowHead.FLAT, x, y, path); } /** * Create the GeneralPath for a figure to be used as the sink endpoint. * * @param double x coordinate * @param double y coordinate * @param GeneralPath reuse a path * * @return GeneralPath */ protected GeneralPath getSinkEndpoint(double x, double y, GeneralPath path) { return ArrowHead.createArrowHead(7.0, ArrowHead.FLAT, x, y, path); } }